package com.seadragon.app.ksr.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.seadragon.app.ksr.dao.BusinessDao;
import com.seadragon.app.ksr.model.Address;
import com.seadragon.app.ksr.model.Business;
import com.seadragon.app.ksr.model.Category;
import com.seadragon.app.ksr.model.CategoryPriceCategorization;
import com.seadragon.app.ksr.model.Office;
import com.seadragon.app.ksr.service.AddressService;
import com.seadragon.app.ksr.service.BusinessService;
import com.seadragon.app.ksr.service.GeocodeService;
import com.seadragon.app.ksr.service.SearchService;
import com.seadragon.app.ksr.util.IndexManager;

@Service("businessService")
@Transactional
public class BusinessServiceImpl implements BusinessService {
	private Logger logger = LoggerFactory.getLogger(BusinessServiceImpl.class);
	@Autowired
	private BusinessDao businessDao;
	
	@Autowired
	private SearchService searchService;

	@Autowired
	private AddressService addressService;

	@Autowired
	private GeocodeService geocodeService;

	@Autowired
	private IndexManager indexManager;
	
	@Override
	public Business update(Business business) {
		Business business2 = businessDao.findById(business.getId());
		if(business.getPassword() == null || business.getPassword().length() == 0){
			business.setPassword(business2.getPassword());
		}else{
			business2.setPassword(business.getPassword());
		}
		business2.setBusinessName(business.getBusinessName());
		business2.setDescription(business.getDescription());
		business2.setEmailAddress(business.getEmailAddress());
		business2.setSpecialty(business.getSpecialty());
		business2.setPhoneNumber(business.getPhoneNumber());
		business2.setWebSite(business.getDescription());
		business2.setDescription(business.getDescription());
		
		business2.setCategories(business.getCategories());
		
		List<Office> customerOffices = business.getOffices();
		List<Office> dbOffices = business2.getOffices();
		List<Office> newOffices = new ArrayList<Office>();
		for(Office office : customerOffices){
			if(office.getId() == 0){
				office.setPriceRange(calculatePriceRange(office));
				Address matchedAddress = searchService.searchAddress(office.getAddress());
				if(matchedAddress == null){
					geocodeService.geocode(office.getAddress());
				}else{
					office.setAddress(matchedAddress);
				}
				office.setBusiness(business2);
				newOffices.add(office);
				continue;
			}
			for(Office dbo : dbOffices){
				if(office.getId() == dbo.getId()){
					boolean addressChanged = false;
					Address address1 = office.getAddress();
					Address address2 = dbo.getAddress();
					if(address1.getAddressLine1() != null && address1.getAddressLine1().length() > 0 
							&& !address1.getAddressLine1().equalsIgnoreCase(address2.getAddressLine1())){
						addressChanged = true;
					}
					if(address1.getAddressLine2() != null && address1.getAddressLine2().length() > 0 
							&& !address1.getAddressLine2().equalsIgnoreCase(address2.getAddressLine2())){
						addressChanged = true;
					}
					if(address1.getCity() != null && address1.getCity().length() > 0 
							&& !address1.getCity().equalsIgnoreCase(address2.getCity())){
						addressChanged = true;
					}
					if(address1.getState() != null && address1.getState().length() > 0 
							&& !address1.getState().equalsIgnoreCase(address2.getState())){
						addressChanged = true;
					}
					if(address1.getZip() != null && address1.getZip().length() > 0 
							&& !address1.getZip().equalsIgnoreCase(address2.getZip())){
						addressChanged = true;
					}
					if(addressChanged){
						Address matchedAddress = searchService.searchAddress(address1);
						if(matchedAddress == null){
							geocodeService.geocode(address1);
							dbo.setAddress(address1);
						}else{
							dbo.setAddress(matchedAddress);
						}
					}

					if(!office.getDescription().equals(dbo.getDescription())){
						dbo.setDescription(office.getDescription());
					}
					if(!office.getEmailAddress().equalsIgnoreCase(dbo.getEmailAddress())){
						dbo.setEmailAddress(office.getEmailAddress());
					}
					if(!office.getPhoneNumber().equalsIgnoreCase(dbo.getPhoneNumber())){
						dbo.setPhoneNumber(office.getPhoneNumber());
					}
					if(office.getPrice() != dbo.getPrice()){
						dbo.setPrice(office.getPrice());
						dbo.setPriceRange(calculatePriceRange(dbo));
					}
					if(!office.getSpecialty().equals(dbo.getSpecialty())){
						dbo.setSpecialty(office.getSpecialty());
					}
					try{
						indexManager.insertDocument(office);
					}catch(Exception ex){
						logger.error(ex.getMessage(), ex);
					}
					newOffices.add(dbo);
					break;
				}
			}
		}

		business2.getOffices().clear();
		business2.getOffices().addAll(newOffices);
		return businessDao.update(business2);
	}

	private int calculatePriceRange(Office office){
		Set<Category> categories = office.getBusiness().getCategories();
		int minPriceRange = 4;
		int priceRange;
		for(Category category : categories){
			CategoryPriceCategorization categorization = category.getPriceCategorizations().iterator().next();
			if(office.getPrice() <= categorization.getOneDollarPrice()){
				priceRange = 1;
			}else if(office.getPrice() <= categorization.getTwoDollarPrice()){
				priceRange = 2;
			}else if(office.getPrice() <= categorization.getThreeDollarPrice()){
				priceRange = 3;
			}else{
				priceRange = 4;
			}
			if(priceRange < minPriceRange) minPriceRange = priceRange;
		}
		return minPriceRange;
	}
	
	@Override
	public void save(Business business) {
		List<Office> offices = business.getOffices();
		for(Office office : offices){
			office.setPriceRange(calculatePriceRange(office));
			Address matchedAddress = searchService.searchAddress(office.getAddress());
			if(matchedAddress == null){
				geocodeService.geocode(office.getAddress());
			}else{
				office.setAddress(matchedAddress);
			}
		}
		businessDao.save(business);
		for(Office office : offices = business.getOffices()){
			try{
				indexManager.insertDocument(office);
			}catch(Exception ex){
				logger.error(ex.getMessage(), ex);
			}
		}
	}

	@Override
	@Transactional(readOnly=true)
	public Business findById(int id) {
		return businessDao.findById(id);
	}

	@Override
	@Transactional(readOnly=true)
	public Business findByEmail(String email) {
		return businessDao.findByEmail(email);
	}

	@Override
	public List<Business> findAll(int offset, int pageSize) {
		return businessDao.findAll(offset, pageSize);
	}

}
